#include "VolumeGridData.h"
#include "OCMain/OCMath.h"

namespace OCPP
{
	namespace Volume
	{
		void VolumeGridData::copyFrom(const VolumeGridData& ncOutput)
		{
			_VolumeExtent = ncOutput._VolumeExtent;
			_unit = ncOutput._unit;
			_fillValue = ncOutput._fillValue;
			_missingValue = ncOutput._missingValue;
		}

		bool VolumeGridData::isValid() const
		{
			return _VolumeExtent.dimX > 1 && _VolumeExtent.dimY > 1;
		}

		bool VolumeGridData::isValidValue(float value) const
		{
			if (std::isnan(value) || std::isinf(value)
				|| value == 9.9990002605540088e+20f)
			{
				return false;
			}

			if (_missingValue.has_value() && Math::floatEqual(value, _missingValue.value(), 1e-3))
			{
				return false;
			}

			if (_fillValue.has_value() && Math::floatEqual(value, _fillValue.value(), 1e-3))
			{
				return false;
			}

			return true;
		}

		size_t VolumeGridData::getSize() const
		{
			return _dataType == Type_Float ? _vecFloat.size() :
				_dataType == Type_Double ? _vecDouble.size() : _vecChar.size();
		}

		float VolumeGridData::getFloat(size_t i) const
		{
			return _dataType == Type_Float ? _vecFloat[i] :
				_dataType == Type_Double ? (float)_vecDouble[i] : _vecChar[i];
		}

		void VolumeGridData::scaleAndOffset(float scale, float offset)
		{
			for (auto& value : _vecFloat)
			{
				value *= scale;
				value += offset;
			}
		}

		void VolumeGridData::ClipIf(const VolumeOptions& options)
		{
			if (
				(_VolumeExtent.xMax != 180.0 && _VolumeExtent.xMin != -180.0)
				&&
				_VolumeExtent.xMax - _VolumeExtent.xMin > 359.0f)
			{
				_VolumeExtent.xMin = -180.0f;
				_VolumeExtent.xMax = 180.0f;
				clampX();
			}

			if (_VolumeExtent.yMin > _VolumeExtent.yMax)
			{
				float tmp = _VolumeExtent.yMin;
				_VolumeExtent.yMin = _VolumeExtent.yMax;
				_VolumeExtent.yMax = tmp;
				reverseY();
			}

			if (options.reverseY.has_value() && options.reverseY.value())
			{
				reverseY();
			}

			if (options.clipRect.has_value())
			{
				float dx = (_VolumeExtent.xMax - _VolumeExtent.xMin) / (_VolumeExtent.dimX - 1);
				float dy = (_VolumeExtent.yMax - _VolumeExtent.yMin) / (_VolumeExtent.dimY - 1);

				CRectangle clipExtent = options.clipRect.value();
				clipExtent = clipExtent.intersect(
					CRectangle(
						_VolumeExtent.xMin,
						_VolumeExtent.yMin,
						_VolumeExtent.xMax,
						_VolumeExtent.yMax));

				CRectangle clipExtentFixed;
				Vector2 minXY = clipExtent.getMinimum();
				Vector2 maxXY = clipExtent.getMaximum();
				clipExtentFixed.getMinimum() = minXY;
				clipExtentFixed.getMaximum() = maxXY;

				float x = _VolumeExtent.xMin;
				for (; x <= _VolumeExtent.xMax - dx; x += dx)
				{
					if (minXY.x >= x && minXY.x < (x + dx))
					{
						clipExtentFixed.getMinimum().x = x;
					}
					if (maxXY.x > x && maxXY.x <= (x + dx))
					{
						clipExtentFixed.getMaximum().x = x + dx;
						break;
					}
				}

				maxXY.x = (std::min)(maxXY.x, x);

				float y = _VolumeExtent.yMin;
				for (; y <= _VolumeExtent.xMax - dy; y += dy)
				{
					if (minXY.y >= y && minXY.y < (y + dy))
					{
						clipExtentFixed.getMinimum().y = y;
					}
					if (maxXY.y > y && maxXY.y <= (y + dy))
					{
						clipExtentFixed.getMaximum().y = y + dy;
						break;
					}
				}

				int xClipStart = round((clipExtentFixed.getMinimum().x - _VolumeExtent.xMin) / dx);
				int yClipStart = round((clipExtentFixed.getMinimum().y - _VolumeExtent.yMin) / dy);
				int newDimX = clipExtentFixed.getWidth() / dx + 1;
				int newDimY = clipExtentFixed.getHeight() / dy + 1;
				int newDimXY = newDimX * newDimY;

				int dimZ = _VolumeExtent.dimZ;
				int dimY = _VolumeExtent.dimY;
				int dimX = _VolumeExtent.dimX;
				int dimXY = dimX * dimY;

				FloatVector fv;
				fv.resize(newDimXY * dimZ);
				for (int z = 0; z < dimZ; z++) {
					int idZ = z * newDimXY;
					int idZ0 = z * dimXY;
					for (int y = 0; y < newDimY; y++) {
						int idY = y * newDimX;
						int idY0 = (y + yClipStart) * dimX;
						for (int x = 0; x < newDimX; x++) {
							int i = idZ + idY + x;
							int i0 = idZ0 + idY0 + x + xClipStart;
							fv[i] = getFloat(i0);
						}
					}
				}

				_vecFloat = std::move(fv);
				_dataType = Type_Float;
				_VolumeExtent.dimX = newDimX;
				_VolumeExtent.dimY = newDimY;
				_VolumeExtent.xMin = clipExtentFixed.getMinimum().x;
				_VolumeExtent.yMin = clipExtentFixed.getMinimum().y;
				_VolumeExtent.xMax = clipExtentFixed.getMaximum().x;
				_VolumeExtent.yMax = clipExtentFixed.getMaximum().y;
			}

			if (options.reverseZ.has_value() && options.reverseZ.value())
			{
				reverseZ();
			}
		}

		void VolumeGridData::clampX()
		{
			if (_dataType != Type_Float)
				_vecFloat.resize(getSize());

			int dimZ = _VolumeExtent.dimZ;
			int dimY = _VolumeExtent.dimY;
			int dimX = _VolumeExtent.dimX;
			for (int z = 0; z < dimZ; z++) {
				int idZ = z * dimY * dimX;
				for (int y = 0; y < dimY; y++) {
					int idY = y * dimX;
					for (int x = 0; x < dimX * 0.5; x++) {
						int i0 = idZ + idY + x;
						int i1 = idZ + idY + x + (int)(0.5 * dimX);
						assert(x + 0.5 * dimX != dimX);
						float value0 = getFloat(i0);
						float value1 = getFloat(i1);
						_vecFloat[i0] = value1;
						_vecFloat[i1] = value0;
					}
				}
			}

			_dataType = Type_Float;
		}

		void VolumeGridData::reverseY()
		{
			if (_dataType != Type_Float)
				_vecFloat.resize(getSize());

			int dimZ = _VolumeExtent.dimZ;
			int dimY = _VolumeExtent.dimY;
			int dimX = _VolumeExtent.dimX;
			for (int z = 0; z < dimZ; z++) {
				int idZ = z * dimY * dimX;
				for (int y = 0; y < dimY * 0.5; y++) {
					int idY = y * dimX;
					int idY0 = (dimY - y - 1) * dimX;
					for (int x = 0; x < dimX; x++) {
						int i0 = idZ + idY + x;
						int i1 = idZ + idY0 + x;
						float value0 = getFloat(i0);
						float value1 = getFloat(i1);
						_vecFloat[i0] = value1;
						_vecFloat[i1] = value0;
					}
				}
			}

			_dataType = Type_Float;
		}

		void VolumeGridData::reverseZ()
		{
			int dimZ = _VolumeExtent.dimZ;

			if (dimZ < 2) return;

			if (_dataType != Type_Float)
				_vecFloat.resize(getSize());

			int dimY = _VolumeExtent.dimY;
			int dimX = _VolumeExtent.dimX;
			for (int z = 0; z < dimZ * 0.5; z++) {
				int idZ0 = z * dimY * dimX;
				int idZ1 = (dimZ - z - 1) * dimY * dimX;
				for (int y = 0; y < dimY; y++) {
					int idY = y * dimX;
					for (int x = 0; x < dimX; x++) {
						int i0 = idZ0 + idY + x;
						int i1 = idZ1 + idY + x;
						float value0 = getFloat(i0);
						float value1 = getFloat(i1);
						_vecFloat[i0] = value1;
						_vecFloat[i1] = value0;
					}
				}
			}

			_dataType = Type_Float;
		}
	}
}


